package org.example.service.domain.impl;

import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.request.AuthRequest;
import me.zhyd.oauth.utils.AuthStateUtils;
import org.example.annotation.SocialId;
import org.example.config.properties.SocialAuthInfoProperties;
import org.example.domain.dto.SocialAuthInfo;
import org.example.domain.dto.SocialType;
import org.example.domain.entity.SocialUserRel;
import org.example.domain.vo.LoginResultVo;
import org.example.enmus.LoginTypeEnum;
import org.example.enmus.SocialAuthEnum;
import org.example.exception.BizException;
import org.example.service.domain.RedisServer;
import org.example.service.domain.RestAuthService;
import org.example.service.domain.SocialUserRelService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.Map;
import java.util.Objects;

@Service
public class RestAuthServiceImpl implements RestAuthService {
    

    private static final Logger LOGGER = LoggerFactory.getLogger(RestAuthServiceImpl.class);

    @Resource
    private SocialAuthInfoProperties socialAuthInfoProperties;

    @Resource
    private SocialUserRelService socialUserRelService;
    @Resource
    private SysUserServiceImpl sysUserService;
    @Resource
    private RedisServer redisServer;
    

    @Override
    public String getAuthLinkUrl(String socialCode,Integer loginType) {
        //1 根据code获取key
        SocialAuthEnum socialEnum = SocialAuthEnum.getSocialEnum(socialCode);
        if (Objects.isNull(socialEnum)) {
            throw new BizException("系统不支持的第三方登录");
        }
        //2 获取social认证client,生成认证链接
        try {
            AuthRequest authRequest = getAuthRequest(socialEnum,loginType);
            String authUrl = authRequest.authorize(AuthStateUtils.createState());
            LOGGER.info("getAuthLinkUrl:{}", authUrl);
            return authUrl;
        } catch (Exception e) {
            LOGGER.error("创建认证链接失败,socialCode:{}", socialCode, e);
            throw new BizException("创建认证链接失败");
        }
    }

    @Override
    public AuthRequest getAuthRequest(String socialCode,Integer loginType) {
        SocialAuthEnum socialEnum = SocialAuthEnum.getSocialEnum(socialCode);
        try {
            return getAuthRequest(socialEnum,loginType);
        } catch (Exception e) {
            LOGGER.error("获取认证客户端失败", e);
            throw new BizException("获取认证客户端失败");
        }
    }

    @Override
    public LoginResultVo callback(AuthCallback callback, String socialCode,Integer loginType) {
        SocialAuthEnum socialEnum = SocialAuthEnum.getSocialEnum(socialCode);
        if (Objects.isNull(socialEnum)) {
            throw new BizException("系统不支持的第三方登录");
        }
        AuthRequest authRequest = null;
        try {
            authRequest = getAuthRequest(socialEnum,loginType);
        }  catch (Exception e) {
            LOGGER.error("callback error:", e);
            throw new BizException("获取认证客户端失败");
        }
        Class<?> resposeClass = socialEnum.getResposeClass();
        Field[] declaredFields = resposeClass.getDeclaredFields();
        Field socialIdField = Arrays.stream(declaredFields)
                .filter(field -> {
                    field.setAccessible(true);
                    return Objects.nonNull(field.getAnnotation(SocialId.class));
                })
                .findFirst()
                .orElseThrow(() -> new BizException("系统不支持的第三方登录"));
        String socialIdFieldName = socialIdField.getName();
        AuthResponse response = authRequest.login(callback);
        Object data = response.getData();
        if (Objects.isNull(data)) {
            LOGGER.info("auth response:{}",JSONObject.toJSONString(response));
            throw new BizException("认证失败");
        }
        JSONObject jsonObject = JSONObject.parseObject(JSONObject.toJSONString(data));
        String socialId = ((String) jsonObject.get(socialIdFieldName));
        if (StrUtil.isBlank(socialId)) {
            LOGGER.info("callback socialId:{}为空",socialId);
            throw new BizException("系统错误，请稍后再试");
        }
        SocialUserRel socialUserRel = socialUserRelService.selectBySocialIdAndType(socialId,loginType);
        //表示用户不是第一次登陆系统，返回用户已注册的信息
        if (Objects.nonNull(socialUserRel)) {
            return sysUserService.loginResult(socialUserRel.getUserId(),loginType);
        }
        //用户第一次进入系统，前端进行跳转，进入注册页面，UUID携带给前端,用户注册将UUID携带回来
        return sysUserService.registerByThirdParity(socialId,UUID.randomUUID().toString());
    }

    //TODO redis缓存
    private AuthRequest getAuthRequest(SocialAuthEnum socialAuthEnum,Integer loginType) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        // 根据key获取对应的social信息
        Map<String, SocialType> info = socialAuthInfoProperties.getInfo();
        String key = LoginTypeEnum.getKeyByType(loginType);
        if (StrUtil.isBlank(key)) {
            throw new BizException("非法的登录类型");
        }
        SocialType socialType = info.get(socialAuthEnum.getKey());
        SocialAuthInfo socialAuthInfo = socialType.getSocialAuthInfo(loginType);
        // 根据key获取social认证客户端
        AuthConfig configBuilder = AuthConfig.builder()
                .clientId(socialAuthInfo.getClientId())
                .clientSecret(socialAuthInfo.getClientSecret())
                .redirectUri(socialAuthInfo.getRedirectUri())
                .build();
        Class<? extends AuthRequest> clazz = socialAuthEnum.getClazz();
        Constructor<? extends AuthRequest> constructor = clazz.getDeclaredConstructor(AuthConfig.class);
        return constructor.newInstance(configBuilder);
    }
}


